--
--  Copyright 2022 The Open Islands Authors. All Rights Reserved.
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
--
local _M = {
    _VERSION = '0.1'
}

local utils = require "utils"
local table = require "table"
local resolver = require "resty.dns.resolver"
local lrucache = require "resty.lrucache"
local config = require "config"
local log = require("log")
local string = string

local ngx_re_gmatch = ngx.re.gmatch
local ngx_re_find = ngx.re.find

local domain_cache, err = lrucache.new(500)
if not domain_cache then
    error("failed to create the domain cache: " .. (err or "unknown"))
end

function _M.is_ip_address(hostname)
    return ngx_re_find(hostname, [[\d+?\.\d+?\.\d+?\.\d+$]], "jo")
end

function _M.get_ip_address(hostname)
    if _M.is_ip_address(hostname) then
        return hostname, "it is already an IP address"
    end

    local ip = domain_cache:get(hostname)

    if ip then
        return ip, "get hostname "..hostname..":".. ip .." from domain cache"
    end

    local nameservers = _M.get_nameservers()

    if not nameservers then
        return nil, "get nameservers address failed"
    end

    local r, err = resolver:new({
        nameservers = nameservers,
        retrans = 5,
        timeout = 2000,
    })

    if not r then
        return nil, "get resolver error: "..err
    end

    local answers, err = r:query(hostname, {qtype = r.TYPE_A})

    if not answers or answers.errcode then
        return nil, string.format("query domain %s from resolver no answer", hostname)
    end

    for i, ans in ipairs(answers) do
        if ans.address then
            domain_cache:set(hostname, ans.address, config.federatedproxy["domain_cache_ttl"])
            log.debug("get hostname ", hostname, ":", ans.address, " from nameserver")
            return ans.address, "ok"
        end
    end

    return nil, "failed"
end

function _M.get_nameservers()
    local nameservers
    if config.federatedproxy["nameservers"] ~= nil and config.federatedproxy["nameservers"] ~= "" then
        nameservers = utils.string_split(config.federatedproxy["nameservers"], ",")
        if nameservers == nil or #nameservers < 1 then
            log.error("get nameservers from config failed")
        end
        log.debug("get nameservers from config: ", utils.join_table(nameservers, ","))
    else
        nameservers = _M.read_nameservers_from_resolv_conf()
        log.debug("get nameservers from resolv conf: ", utils.join_table(nameservers, ","))
    end
    return nameservers
end

function _M.read_nameservers_from_resolv_conf()
    local text, err = utils.read_file_data('/etc/resolv.conf')
    if text == nil or #text == 0 then
        log.error("read resolv conf data failed, msg: ", err)
    end
    local captures, it, err
    it, err = ngx_re_gmatch(text, [[^nameserver\s+(\d+?\.\d+?\.\d+?\.\d+$)]], "jomi")

    local nameservers = table.new(5, 0)

    for captures, err in it do
        if not err then
            nameservers[#nameservers + 1] = captures[1]
        end
    end
    if nameservers == nil or #nameservers < 1 then
        log.error("get nameservers from resolv conf failed")
    end
    return nameservers
end

return _M

